<?php

define('MAX_A_MODULE', 100);    // 一级模块最多个数
define('MAX_B_MODULE', 15);     // 每个一级模块下的二级模块

/**
 * Class ModuleService
 */
class ModuleService extends Model
{
    private $_customerId = null;
    private $_logicModuleGroup = null;
    private $_logicModuleGroupAction = null;
    private $_logicAction = null;
    private $_logicRole = null;
    private $_logicRoleAction = null;
    private $_logicRoleModuleGroup = null;
    private $_logicSysRole = null;
    private $_logicSysRoleAction = null;
    private $_logicSysRoleModuleGroup = null;
    private $_logicCustomerModuleGroup = null;

    public function __construct()
    {
        $this->_customerId = Session::$user['customerId'];
        $this->_logicModuleGroup = D('ModuleGroup', 'Logic');
        $this->_logicModuleGroupAction = D('ModuleGroupAction', 'Logic');
        $this->_logicAction = D('Action', 'Logic');
        $this->_logicRole = D('Role', 'Logic');
        $this->_logicRoleAction = D('RoleAction', 'Logic');
        $this->_logicRoleModuleGroup = D('RoleModuleGroup', 'Logic');
        $this->_logicSysRole = D('SysRole', 'Logic');
        $this->_logicSysRoleAction = D('SysRoleAction', 'Logic');
        $this->_logicSysRoleModuleGroup = D('SysRoleModuleGroup', 'Logic');
        $this->_logicCustomerModuleGroup = D('CustomerModuleGroup', 'Logic');
    }

    /**
     * 获得一二级及功能点集合
     */
    public function getModules()
    {
        $AModule = $this->getALevelModule();    // 获得单纯的一级模块
        $AModule = $this->getBLevelModule($AModule);    // 获得一级模块和二级模块
        $modules = $this->getModuleAction($AModule);    // 获得一二级模块和功能点

        $Out = [
            'code'  => 0,
            'msg'   => '查询模块数据成功',
            'data'  => [
                'modules'   => $modules
            ]
        ];

        return $Out;
    }

    /**
     * 获得nmp版一级模块
     *
     * @param bool $showAuth    true-此cid下授权的模块/false-所有模块
     * @return array
     */
    public function getALevelModule($showAuth = TRUE)
    {
        $fields = 'id, name, group, description, iconurl, modules, groupUsage, href, pid, beShow';
        $where = [
            'display'   => 1,
            'backend'   => 'nmp',
            'pid'   => 0,
            'beShow'    => 1,   // 要在路由展示
        ];
        $AModule = $this->_logicModuleGroup->selectPage($fields, $where, 'sortOrder', 'desc', 0, MAX_A_MODULE);
        $AModule = !empty($AModule) ? $AModule : [];

        if ($showAuth) {
            $modules = $this->getCustomerModule($this->_customerId);

            // 遍历处理没授权的模块
            foreach ($AModule as $k_AModule => $v_AModule) {
                if (!in_array($v_AModule['group'], $modules)) {
                    unset($AModule[$k_AModule]);
                }
            }
            $AModule = array_values($AModule);
        }
        $AModuleCount = $this->_logicModuleGroup->selectPageCount($where);  // 查询页数没有用到, 要求最大全部查出

        return $AModule;
    }

    /**
     * 获得nmp版各一级模块下相应的二级模块
     *
     * @param array $AModule    一级模块
     * @param bool $showAuth    true-此cid下授权的模块/false-所有模块
     * @return array
     */
    public function getBLevelModule(array $AModule, $showAuth = TRUE)
    {
        $fields = 'id, name, group, description, iconurl, modules, groupUsage, href, pid, beShow';
        $where = [
            'display'   => 1,
            'backend'   => 'nmp',
            'beShow'    => 1,   // 要在路由展示
        ];

        foreach ($AModule as $k_AModule => $v_AModule) {
            $where['pid'] = $v_AModule['id'];

            $BModule = $this->_logicModuleGroup->selectPage($fields, $where, 'sortOrder', 'desc', 0, MAX_B_MODULE);
            $BModule = !empty($BModule) ? $BModule : [];

            if ($showAuth) {
                $modules = $this->getCustomerModule($this->_customerId);

                // 遍历处理没授权的模块
                foreach ($BModule as $k_BModule => $v_BModule) {
                    if (!in_array($v_BModule['group'], $modules)) {
                        unset($BModule[$k_BModule]);
                    }
                }
                $BModule = array_values($BModule);
            }
            $BModuleCount = $this->_logicModuleGroup->selectPageCount($where);  // 查询页数没有用到, 要求最大全部查出

            $AModule[$k_AModule]['children'] = $BModule;
        }

        return $AModule;
    }

    /**
     * 获得模块对应的功能点
     *
     * @param array $AModule    一级模块: 要求包含'children'字段的, 即处理过其二级模块
     * @return array
     */
    public function getModuleAction(array $AModule)
    {
        foreach ($AModule as $k_AModule => $v_AModule) {
            if (empty($v_AModule['children'])) {  // 如果该一级模块不包含二级模块, 则直接查询该一级模块的功能点
                $actions = $this->getModuleActionByModuleId($v_AModule['id']);

                $AModule[$k_AModule]['actions'] = $actions;
            } else {    // 否则遍历查询其二级模块对应的功能点
                foreach ($v_AModule['children'] as $k_BModule => $v_BModule) {
                    $actions = $this->getModuleActionByModuleId($v_BModule['id']);

                    $AModule[$k_AModule]['children'][$k_BModule]['actions'] = $actions;
                }
            }
        }

        return $AModule;
    }

    /**
     * 根据moduleId获得该module对应的功能点
     *
     * @param $moduleId
     * @return array
     */
    public function getModuleActionByModuleId($moduleId)
    {
        $actionIds = $this->_logicModuleGroupAction->getActionByModuleId($moduleId);
        if (empty($actionIds)) return [];

        $actions = $this->_logicAction->getActionNamesByIds($actionIds);
        $actions = !empty($actions) ? $actions : [];

        return $actions;
    }

    /**
     * 根据指定cid查询该cid下的模块
     *
     * @param $cid
     * @return mixed
     */
    public function getCustomerModule($cid)
    {
        $modules = $this->_logicCustomerModuleGroup->getModuleByCid($cid);
        $modules = !empty($modules) ? $modules : [];

        // 处理这里面二级模相应的一级模块
        if (!empty($modules)) {
            $pids = $this->_logicModuleGroup->getFieldWithWhereDistinct('pid', ['group' => ['IN', $modules], 'pid' => ['NEQ', 0]], 'pid');
            $pids = array_column($pids, 'pid');

            $groups = $this->_logicModuleGroup->getFieldWithWhereDistinct('group', ['id' => ['IN', $pids]], 'group');
            $groups = array_column($groups, 'group');
            $groups = !empty($groups) ? $groups : [];

            $modules = array_merge($modules, $groups);
        }

        return $modules;
    }

    // 以下是处理模块授权时, 模块 - 角色 - 功能点的对应关系

    /**
     * 授权一个模块时, 授权该模块响应的角色, 以及角色对应的action
     *
     * 查询module对应的role => 查询该cid现有role => 过滤出需要增加的role => 增加role
     * 查询增加的role对应的action => 查询该cid现有action => 过滤出需要增加的action => 增加action
     *
     * @param $group
     * @param $cid
     * @return array
     */
    public function configModuleRole($group, $cid)
    {
        // 模块的id
        $groupId = $this->_logicModuleGroup->getFieldWithWhere('id', ['group' => $group, 'display' => 1]);

        /** 处理role */
        // 根据模块id查询模块对应的roleIds
        $sysRoleIds = $this->_logicSysRoleModuleGroup->getRoleIdsByGroupId($groupId);

        // 查询当前cid下现有的role
        $arr_englishRoles = $this->_logicRole->getField(['cid' => $cid], 'englishRole', TRUE);
        $arr_englishRoles = !empty($arr_englishRoles) ? $arr_englishRoles : [];

        // 处理roleId和englishRole对应
        $arrSysRole = $this->setSysEnglishRoleWithRoleIds($sysRoleIds);
        $arr_sysEngilshRole = array_column($arrSysRole, 'englishRole');

        //  过滤新增role
        $newRole = [];
        foreach ($arr_sysEngilshRole as $k_sysEnglishRole => $v_sysEnglishRole) {
            if (!in_array($v_sysEnglishRole, $arr_englishRoles)) {   // 当前不存在该role
                array_push($newRole, $arrSysRole[$k_sysEnglishRole]);
            }
        }

        // 如果无新增role, 则可以返回成功, 否则下方in查询错误
        if (empty($newRole)) {
            $Out = [
                'code'  => 0,
                'msg'   => '此次无新增role',
                'data'  => []
            ];
            return $Out;
        } else {
            M()->startTrans();
            foreach ($newRole as $k_role => $v_role) {
                // 添加角色
                $data_role = [
                    'cid'   => $cid,
                    'englishRole'   => $v_role['englishRole'],
                    'chineseRole'   => $v_role['chineseRole'],
                    'manual'   => $v_role['manual'],
                    'isSys' => 1,
                    'auage' => $v_role['auage']
                ];
                $res_roleId = $this->_logicRole->addRole($data_role);
                if (!is_numeric($res_roleId)) {
                    \Log::write('$data_role = ' . json_encode($data_role));
                    \Log::write('$this->_logicRole_sql = ' . $this->_logicRole->getLastSql());
                    M()->rollback();
                    $Out = [
                        'code'  => 505,
                        'msg'   => '数据读写错误 - 角色添加失败',
                        'data'  => []
                    ];
                    return $Out;
                }

                // 添加角色与模块的对应
                $data_roleModuleGroup = [
                    'cid' => $cid,
                    'groupId' => $groupId,
                    'agent' => $res_roleId
                ];
                $res_roleModuleGroupId = $this->_logicRoleModuleGroup->addRoleModuleGroup($data_roleModuleGroup);
                if (!is_numeric($res_roleModuleGroupId)) {
                    \Log::write('$data_roleModuleGroup = ' . json_encode($data_roleModuleGroup));
                    \Log::write('$this->_logicRoleModuleGroup_sql = ' . $this->_logicRoleModuleGroup->getLastSql());
                    M()->rollback();
                    $Out = [
                        'code'  => 505,
                        'msg'   => '数据读写错误 - 角色添加失败',
                        'data'  => []
                    ];
                    return $Out;
                }

                /** 处理action */
                $d_action = ['cid' => $cid, 'roleId' => $res_roleId];    // 数据cid, roleId

                $sysActionIds = $this->_logicSysRoleAction->selectFieldsWithWhere('actionId', ['sysRoleId' => $v_role['id']]);
                $sysActionIds = !empty($sysActionIds) ? $sysActionIds : [];
                $sysActionIds = array_column($sysActionIds, 'actionId');

                // 过滤需要新增的action
                $actionIds = $this->_logicRoleAction->selectFieldsWithWhere('actionId', ['roleId' => $v_role['id']]);
                $actionIds = !empty($actionIds) ? $actionIds : [];
                $actionIds = array_column($actionIds, 'actionId');  // actionIds
                $addRoleIds = array_values(array_diff($sysActionIds, $actionIds));  // 要新增的actionIds

                // 如果该角色下已有改action, 则继续
                if (empty($addRoleIds)) {
                    continue;
                }

                $data_roleAction = [];
                foreach ($addRoleIds as $k_addRoleId => $v_addRoleId) {
                    $d_action = array_merge($d_action, ['actionId' => $v_addRoleId]);   // 数据actionId

                    array_push($data_roleAction, $d_action);
                }
                $res_roleAction = $this->_logicRoleAction->addAll($data_roleAction);
                if (!is_numeric($res_roleAction)) {
                    \Log::write('$data_roleAction = ' . json_encode($data_roleAction));
                    \Log::write('$this->_logicRoleAction_sql = ' . $this->_logicRoleAction->getLastSql());
                    M()->rollback();
                    $Out = [
                        'code'  => 505,
                        'msg'   => '数据读写错误 - 角色对应功能添加失败',
                        'data'  => []
                    ];
                    return $Out;
                }
            }
        }

        /** 返回 */
        M()->commit();
        $Out = [
            'code'  => 0,
            'msg'   => '授权时处理模块-角色-功能点成功',
            'data'  => []
        ];
        return $Out;
    }

    /**
     * 取消授权一个模块时, 取消改模块下角色的授权, 以及角色对应的action
     *
     * 查询module对应的role => 循环role列表, 查询role对应的其他模块是否依旧在cid下 =>
     * 如果只剩这个role只对应这个module, 并且module被取消了, 则撤回该role => 并且撤回role对应的action =>
     * 无论如何撤回module - role的对应
     *
     * @param $group
     * @param $cid
     * @return array
     */
    public function cancelConfigModuleRole($group, $cid)
    {
        // 模块的id
        $groupId = $this->_logicModuleGroup->getFieldWithWhere('id', ['group' => $group, 'display' => 1]);

        // 查询这个groupId对应的roles
        $roleIds = $this->_logicRoleModuleGroup->getField(['cid' => $cid, 'groupId' => $groupId], 'agent', TRUE);

        // 删除这个module - role的对应
        M()->startTrans();
        $res_delRoleModuleGroup = $this->_logicRoleModuleGroup->deleteWithWhere(['cid' => $cid, 'groupId' => $groupId]);
        if (!is_numeric($res_delRoleModuleGroup)) {
            \Log::write('$res_delRoleModuleGroup = ' . $res_delRoleModuleGroup);
            \Log::write('$this->_logicRoleModuleGroup_sql = ' . $this->_logicRoleModuleGroup->getLastSql());
            M()->rollback();
            $Out = [
                'code'  => 505,
                'msg'   => '数据读写错误 - 删除模块与角色关联错误',
                'data'  => []
            ];
            return $Out;
        }

        // 遍历这个groupId对应的roleId进行处理

        foreach ($roleIds as $k_roleId => $v_roleId) {
            // 如果这个模块只与这个这个角色关联, 则该模块在上方删除后, 这里查询应该为空
            $groupIds = $this->_logicRoleModuleGroup->getField(['cid' => $cid, 'agent' => $v_roleId], 'groupId', TRUE);

            if (empty($groupIds)) { // 这个role只在这个module中使用
                // 删除角色
                // 删除角色, 因为wx_role表没有存roleId, 所以需要通过englishRole等字段进行判断, 所以先组织这个roleId的数据
                $role = $this->setEnglishRoleWithRoleIds([ $v_roleId ]);
                if (empty($role)) {
                    \Log::write('$role = ' . $role);
                    M()->rollback();
                    $Out = [
                        'code'  => 505,
                        'msg'   => '数据读写错误 - 删除模块下角色数据不足',
                        'data'  => []
                    ];
                    return $Out;
                }

                $role = $role[0];   // 这里必然不能为空
                $res_delRole = $this->_logicRole->deleteWithWhere(['cid' => $cid, 'englishRole' => $role['englishRole'], 'isSys' => 1]);
                if (!is_numeric($res_delRole)) {
                    \Log::write('$res_delRole = ' . $res_delRole);
                    \Log::write('$this->_logicRole_sql = ' . $this->_logicRole->getLastSql());
                    M()->rollback();
                    $Out = [
                        'code'  => 505,
                        'msg'   => '数据读写错误 - 删除模块下角色错误',
                        'data'  => []
                    ];
                    return $Out;
                }

                // 删除action
                $res_delRoleAction = $this->_logicRoleAction->deleteWithWhere(['cid' => $cid, 'roleId' => $v_roleId]);
                if (!is_numeric($res_delRoleAction)) {
                    \Log::write('$res_delRoleAction = ' . $res_delRoleAction);
                    \Log::write('$this->_logicRoleAction_sql = ' . $this->_logicRoleAction->getLastSql());
                    M()->rollback();
                    $Out = [
                        'code'  => 505,
                        'msg'   => '数据读写错误 - 删除模块下角色对应的action错误',
                        'data'  => []
                    ];
                    return $Out;
                }
            }
        }

        /** 返回 */
        M()->commit();
        $Out = [
            'code'  => 0,
            'msg'   => '取消模块配置模块-角色-功能点成功',
            'data'  => []
        ];
        return $Out;
    }

    /**
     * 根据roleIds数据, 处理roleId和englishRole的对应
     *
     * @param $roleIds
     * @return mixed
     */
    public function setSysEnglishRoleWithRoleIds($roleIds)
    {
        $fields = 'id, englishRole, chineseRole, manual, auage';
        if (count($roleIds) == 1) {
            $where = ['id' => $roleIds[0]];
        } else {
            $where = ['id' => ['IN', $roleIds]];
        }

        return $this->_logicSysRole->selectFieldsWithWhere($fields, $where);
    }

    /**
     * 根据roleIds数据, 处理roleId和englishRole的对应
     *
     * @param $roleIds
     * @return mixed
     */
    public function setEnglishRoleWithRoleIds($roleIds)
    {
        $fields = 'id, cid, englishRole, chineseRole, manual, auage';
        if (count($roleIds) == 1) {
            $where = ['id' => $roleIds[0]];
        } else {
            $where = ['id' => ['IN', $roleIds]];
        }

        return $this->_logicRole->selectFieldsWithWhere($fields, $where);
    }

}